package com.imooc.security.core.validate.code.impl;

import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.social.connect.web.HttpSessionSessionStrategy;
import org.springframework.social.connect.web.SessionStrategy;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.ServletRequestUtils;
import org.springframework.web.context.request.ServletWebRequest;

import com.imooc.security.core.validate.code.ValidateCode;
import com.imooc.security.core.validate.code.ValidateCodeGenerator;
import com.imooc.security.core.validate.code.ValidateCodeProcessor;
import com.imooc.security.core.validate.code.ValidateCodeType;
import com.imooc.security.core.validate.code.exception.ValidateCodeException;

/**
 * 抽象的模板方法
 * @author JZH
 *
 */
public abstract class AbstractValidateCodeProcessor<T extends ValidateCode> implements ValidateCodeProcessor {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	/**
	 * session工具类，用于将验证码存储到session中
	 */
	private SessionStrategy sessionStrategy = new HttpSessionSessionStrategy();
	
	@Autowired
	private Map<String, ValidateCodeGenerator> validateCodeGenerators;
	
	@Override
	public final void createCode(ServletWebRequest request)  throws Exception {
		// TODO Auto-generated method stub
		T validateCode = generate(request);
		save(request, validateCode);
		send(request, validateCode);
	}
	
	/**
	 * 验证码校验逻辑
	 */
	@Override
	public void validate(ServletWebRequest request) {
		// TODO Auto-generated method stub
		ValidateCodeType codeType = getValidateCodeType();
		String sessionKey = getValidateCodeSessionKey();
		@SuppressWarnings("unchecked")
		T codeInSession = (T) sessionStrategy.getAttribute(request, sessionKey);
		String codeInRequest = "";
		try {
			codeInRequest = ServletRequestUtils.getStringParameter(request.getRequest(), codeType.getParamNameOnValidate());
			logger.info("获取Request中的参数；key："+codeType.getParamNameOnValidate()+"-value："+codeInRequest);
		} catch (ServletRequestBindingException e) {
			throw new ValidateCodeException("获取验证码的值失败");
		}
		if (StringUtils.isBlank(codeInRequest)) {
			throw new ValidateCodeException(codeType + "验证码的值不能为空");
		}
		if (codeInSession == null) {
			throw new ValidateCodeException(codeType + "验证码不存在");
		}
		if (codeInSession.isExpried()) {
			sessionStrategy.removeAttribute(request, sessionKey);
			throw new ValidateCodeException(codeType + "验证码已过期");
		}
		if (!StringUtils.equals(codeInSession.getCode(), codeInRequest)) {
			throw new ValidateCodeException(codeType + "验证码不匹配");
		}
		sessionStrategy.removeAttribute(request, sessionKey);
	}
	
	/**
	 * 生成校验码
	 * 使用依赖查找的方式选择ValidateCodeGenerator的实现类
	 * @param request
	 * @return
	 */
	public T generate(ServletWebRequest request) {
		String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor").toLowerCase();
		String generatorName = type+ValidateCodeGenerator.class.getSimpleName();
		ValidateCodeGenerator generator = validateCodeGenerators.get(generatorName);
		if(null == generator) {
			throw new ValidateCodeException("验证码生成器"+generatorName+"不存在");
		}
		@SuppressWarnings("unchecked")
		T t = (T) generator.generate(request.getRequest());
		return t;
	}
	
	/**
	 * 将校验码放入session中
	 * @param request
	 * @param t 校验码
	 */
	public void save(ServletWebRequest request, T t) {
		String key = getValidateCodeSessionKey();
		sessionStrategy.setAttribute(request, key, t);
	}
	
	/**
	 * 发送校验码给客户端
	 * @param request
	 * @param t
	 */
	public abstract void send(ServletWebRequest request, T t) throws Exception;
	
	/**
	 * 获取校验码在session中的key
	 * @return
	 */
	private String getValidateCodeSessionKey() {
		String key = SESSION_KEY_PREFIX +getValidateCodeType();
		return key;
	}
	
	/**
	 * 获取本次请求的类型
	 * 请求可能是 生成验证码和校验验证码
	 * 类型可能是 图形验证码和短信验证码
	 * @return
	 */
	private ValidateCodeType getValidateCodeType() {
		String type = StringUtils.substringBefore(getClass().getSimpleName(), "CodeProcessor");
		return ValidateCodeType.valueOf(type.toUpperCase());
	}

}
